#ifndef PROTOQTRPC_LOCATIONCLIENT_H
#define PROTOQTRPC_LOCATIONCLIENT_H

#include <QObject>
#include <QDateTime>
#include <QHostAddress>
#include <QList>

namespace google {
namespace protobuf {
class Service;
}
}

namespace protoqtrpc {
namespace location {

class LocationServiceClientPrivate;

/**
 * @brief The LocationServiceClient class.
 *  This is a convenient wrapper around LocationService protocol.
 */
class LocationServiceClient: public QObject
{
    Q_OBJECT

public:
    /**
     * @brief Status of call execution
     */
    enum Status
    {
        STATUS_SUCCESS = 0,
        STATUS_ERROR_OTHER = 1,
        STATUS_ERROR_NO_FREE_PORTS = 2,
        STATUS_ERROR_NOT_REGISTERED = 3,
        STATUS_ERROR_ALREADY_REGISTERED = 4
    };

    /**
     * @brief Information about the server
     */
    struct ServerInfo
    {
        QString name;               //!< name of server
        QString description;        //!< description of server
        QHostAddress host;          //!< IPv4 host address of server
        quint16 port;               //!< port of server
        QDateTime time;             //!< registration time
    };

    /**
     * @brief Information about the service
     */
    struct ServiceInfo
    {
        QString name;               //!< full name of service
        QString description;        //!< description of service
        QDateTime time;             //!< registration time
    };

    /**
     * @brief Information about the services that is registered on server
     */
    struct ServerServicesInfo
    {
        ServerInfo serverInfo;      //!< server information
        QList<ServiceInfo> servicesInfo;  //!< list of registered services for this server
    };

    /**
     * @brief Location of the service
     */
    struct ServiceLocation
    {
        ServerInfo serverInfo;    //!< information about the server where that service is registered
        ServiceInfo serviceInfo;  //!< information about the service found
    };

public:
    /**
     * @brief Construct LocationServiceClient to connect to LocationService
     * @param host Host on which LocationService is started
     * @param port Port on which LocationService is started, 0 - for default port
     */
    LocationServiceClient(const QHostAddress& host = QHostAddress::LocalHost,
                          quint16 port = 0);

    ~LocationServiceClient();

    /**
     * @brief Sets session timeout.
     *  Connection will be closed after "timeout" milliseconds
     *  after last call to LocationService.
     * @param timeout Timeout in millisecods
     */
    void setSessionTimeout(int timeout = 10000);

    /**
     * @brief Gets session timeout.
     * @return Session timeout
     */
    int getSessionTimeout() const;

    /**
     * @brief Get the host on which LocationService is started
     * @return Host
     */
    const QHostAddress& serverHost() const;

    /**
     * @brief Get the port on which LocationService is started
     * @return Port
     */
    quint16 serverPort() const;

    /**
     * @brief Registers the server
     * Add server to registry, allocate and obtain server port
     * Right port will be set when status is
     * one of STATUS_SUCCESS or STATUS_ERROR_ALREADY_REGISTERED
     *
     * @param serverName Name of the server to register
     * @param description Server description
     * @param port Resulting port that was allocated for server,
     * @return Status of request execution
     */
    Status registerServer(const QString& serverName,
                          const QString& description,
                          quint16& port);

    /**
     * @brief Unregisters the server
     * Remove server from registry, unallocate server port
     *
     * @param serverName Name of the server to unregister
     * @return Status of request execution
     */
    Status unregisterServer(const QString& serverName);

    /**
     * @brief Gets server info
     * Get server information by name
     *
     * @param serverName Server name
     * @param serverInfo Resulting server info
     * @return Status of request execution
     */
    Status getServerInfo(const QString& serverName,
                         ServerInfo& serverInfo);

    /**
     * @brief Gets server information with services by name
     * @param serverName Server name
     * @param info Resulting server services info
     * @return Status of request execution
     */
    Status getServerServicesInfo(const QString& serverName,
                                 ServerServicesInfo& info);

    /**
     * @brief Get all known servers information with services
     * @param serverServicesInfo Resulting servers info
     * @return Status of request execution
     */
    Status getServersInfo(QList<ServerServicesInfo>& infos);


    /**
     * @brief Add service to registry
     * @param serverName Server name
     * @param serviceName Fully-qualified name of the service
     * @param description Service description
     * @return Status of request execution
     */
    Status registerService(const QString& serverName,
                           const QString& serviceName,
                           const QString& serviceDescription);

    /**
     * @brief Remove service from registry
     * @param serverName Server name
     * @param serviceName Fully-qualified name of the service
     * @return Status of request execution
     */
    Status unregisterService(const QString& serverName,
                             const QString& serviceName);

    /**
     * @brief Get service information by full service name
     * @return Status of request execution
     */
    Status getServiceInfo(const QString& serverName,
                          const QString& serviceName,
                          ServiceInfo& serviceInfo);

    /**
     * @brief Locate service by full name
     * @param serviceName Full name of the service
     * @param serviceLocations Resulting list of locations where that service is registered
     * @param serverHost Host on which stared a server where to search a service
     * @param serverName Name of the server where this service is registered
     * @return Status of request execution
     */
    Status locateService(const QString& serviceName,
                         QList<ServiceLocation>& serviceLocations,
                         const QHostAddress& serverHost = QHostAddress::Any,
                         const QString& serverName = QString());

private:
    Q_DECLARE_PRIVATE(LocationServiceClient)
    Q_DISABLE_COPY(LocationServiceClient)
    LocationServiceClientPrivate* d_ptr;
};

}
}

#endif // PROTOQTRPC_LOCATIONCLIENT_H
